<?php

// $Id:$

/**
 * @file
 * Support file for node_import module for Ubercart products (uc_product).
 */

/**
 * Implementation of hook_node_import_fields().
 */
function uc_product_node_import_fields($type) {
    if (_uc_product_node_import_is_product($type)) {
        $fields = array(
            'title' => 'Product: Name',
            'body' => 'Product: Description',
            'model' => 'Product: SKU',
            'list_price' => 'Product: List price',
            'cost' => 'Product: Cost price',
            'sell_price' => 'Product: Sell price',
            'weight' => 'Product: Weight',
            'weight_units' => 'Product: Weight units',
            'length' => 'Product: Length',
            'width' => 'Product: Width',
            'height' => 'Product: Height',
            'length_units' => 'Product: Dimension units',
            'pkg_qty' => 'Product: Package quantity',
            'default_qty' => 'Product: Default quantity',
            'ordering' => 'Product: List position',
            'shippable' => 'Product: Shippable',
            'type' => 'Product: Product type/class',
            'image' => 'Product: Main Image',
        );

        if (module_exists('uc_attribute')) {
            //add attribute fields
            //get all attribute ids
            $db_result = db_query("SELECT aid FROM {uc_attributes}");
            $attribute_ids = array();
            while ($aid = db_fetch_object($db_result))
                $attribute_ids[] = $aid->aid;
            //get options for each attribute and add to fields array
            foreach ($attribute_ids as $aid) {
                $attribute = uc_attribute_load($aid);
                foreach (array('enabled', 'required', 'ordering') as $f) {
                    $fields['ucatr:' . $attribute->aid . ':' . $f] = 'Product attr.: ' . $attribute->name . ': ' . $f;
                }
                foreach ($attribute->options as $option) {
                    foreach (array('enabled', 'cost', 'price', 'weight', 'ordering') as $f) {
                        $fields['ucatr:' . $attribute->aid . ':' . $option->oid . ':' . $f] = 'Product attr.: ' . $attribute->name . ': ' . $option->name . ': ' . $f;
                    }
                }
            }
        }
        return $fields;
    }
}

/**
 * Implementation of hook_node_import_prepare().
 */
function uc_product_node_import_prepare(&$node, $preview = FALSE) {
    $errors = array();
    if (_uc_product_node_import_is_product($node->type)) {
        $currency_error = t(' is not valid. It must only contain digits, an optional decimal marker (%decimal), an optional currency symbol (%currency) and optional thousands markers (%thousand).', array('%decimal' => variable_get('uc_currency_dec', '.'), '%currency' => variable_get('uc_currency_sign', '$'), '%thousand' => variable_get('uc_currency_thou', ',')));

        //NAME and SKU

        if (!isset($node->title) || $node->title == '') {
            $errors[] = t('Product name is required.');
        }

        if (!isset($node->model) || $node->model == '') {
            $errors[] = t('Product SKU is required.');
        }

        //COST / SELL / PRICE

        if (isset($node->cost)) {
            $cost = _uc_product_normalize_price($node->cost);
            if ($cost === FALSE) {
                $errors[] = t('Cost price \'%value\'', array('%value' => check_plain($node->cost))) . $currency_error;
            } else {
                $node->cost = $cost;
            }
        }

        if (!isset($node->sell_price) || $node->sell_price == '') {
            $errors[] = t('Sell price is required');
        } else {
            $sell_price = _uc_product_normalize_price($node->sell_price);
            if ($sell_price === FALSE) {
                $errors[] = t('Sell price \'%value\'', array('%value' => check_plain($node->sell_price))) . $currency_error;
            } else {
                $node->sell_price = $sell_price;
            }
        }

        if (isset($node->list_price)) {
            $list_price = _uc_product_normalize_price($node->list_price);
            if ($list_price === FALSE) {
                $errors[] = t('List price \'%value\'', array('%value' => check_plain($node->list_price))) . $currency_error;
            } else {
                $node->list_price = $list_price;
            }
        }

        //WEIGHT and DIMENSIONS

        foreach (array('weight' => 'Weight', 'length' => 'Length', 'width' => 'Width', 'height' => 'Height') as $field => $label) {
            if (isset($node->$field)) {
                if ($node->$field == '')
                    $node->$field = 0;
                else if (!is_numeric($node->$field))
                    $errors[] = t('!label \'%value\' is not valid. It must be a number.', array('%value' => check_plain($node->$field), '!label' => t($label)));
                else if ($node->length < 0)
                    $errors[] = t('!label \'%value\' is not valid. It must be greater than or equal to 0.', array('%value' => check_plain($node->$field), '!label' => t($label)));
            }
        }

        if (isset($node->weight_units)) {
            $node->weight_units = strtolower($node->weight_units);
            if (!is_valid_weight_unit($node->weight_units))
                $errors[] = t('Weight unit \'%value\' is not valid. It must be one of %units.', array('%value' => check_plain($node->weight_units), '%units' => join(valid_weight_units(), ', ')));
        }

        if (isset($node->length_units)) {
            $node->length_units = strtolower($node->length_units);
            if (!is_valid_dimension_unit($node->length_units))
                $errors[] = t('Dimensions unit \'%value\' is not valid. It must be one of %units.', array('%value' => check_plain($node->length_units), '%units' => join(valid_dimension_units(), ', ')));
        }

        //PACKAGE and DEFAULT QUANTITIES

        foreach (array('pkg_qty' => 'Package quantity', 'default_qty' => 'Default quantity') as $field => $label) {
            if (!isset($node->$field) || $node->$field == '')
                $node->$field = 1;
            else if (!is_intval($node->$field))
                $errors[] = t('!label \'%value\' is not valid. It must be an integer.', array('%value' => check_plain($node->$field), '!label' => t($label)));
            else if ($node->$field < 0)
                $errors[] = t('!label \'%value\' is not valid. It must be greater than or equal to 0.', array('%value' => check_plain($node->$field), '!label' => t($label)));
        }

        //ORDERING

        if (!isset($node->ordering) || $node->ordering == '')
            $node->ordering = 0;
        else if (!is_intval($node->ordering))
            $errors[] = t('List position \'%value\' is not valid. It must be an integer (either positive or negative).', array('%value' => check_plain($node->ordering)));
        //don't do range check here because nothing will break if it's greater than the range allowed by the product editing form, and it allows more flexibility
        //SHIPPABLE

        if (!is_bool_value($node->shippable))
            $errors[] = t('Shippable value \'%value\' is not valid. It must be one of %bools.', array('%value' => check_plain($node->shippable), '%bools' => join(bool_values(), ', ')));
        else
            $node->shippable = is_true_value($node->shippable);

        //ATTRIBUTES

        if (module_exists('uc_attribute')) {
            foreach ($node as $key => $value) {
                if (stripos($key, 'ucatr') === 0) {
                    $attr = explode(':', $key);
                    if (count($attr) == 3) { //attribute values
                        switch ($attr[2]) {
                            case 'enabled':
                                if (!is_bool_value($value))
                                    $errors[] = t('Attribute enabled value \'%value\' is not valid. It must be one of %bools.', array('%value' => check_plain($value), '%bools' => join(bool_values(), ', ')));
                                else
                                    $node->$key = is_true_value($value);
                                break;
                            case 'required':
                                if (!is_bool_value($value))
                                    $errors[] = t('Attribute required value \'%value\' is not valid. It must be one of %bools.', array('%value' => check_plain($value), '%bools' => join(bool_values(), ', ')));
                                else
                                    $node->$key = is_true_value($value);
                                break;
                            case 'ordering':
                                if (!is_intval($value))
                                    $errors[] = t('Attribute ordering value \'%value\' is not valid. It must be an integer.', array('%value' => check_plain($value)));
                                else if ($value < -10 || $value > 10)
                                    $errors[] = t('Attribute ordering value \'%value\' is not valid. It must be in the range -10 to 10.', array('%value' => check_plain($value)));
                                break;
                        }
                    } else { //attribute option values
                        switch ($attr[3]) {
                            case 'enabled':
                                if (!is_bool_value($value))
                                    $errors[] = t('Attribute option enabled value \'%value\' is not valid. It must be one of %bools.', array('%value' => check_plain($value), '%bools' => join(bool_values(), ', ')));
                                else
                                    $node->$key = is_true_value($value);
                                break;

                            case 'cost':
                                $cost = _uc_product_normalize_price($value);
                                if ($cost === FALSE)
                                    $errors[] = t('Attribute option cost \'%value\'', array('%value' => check_plain($value))) . $currency_error;
                                else
                                    $node->$key = $cost;
                                break;

                            case 'price':
                                $price = _uc_product_normalize_price($value);
                                if ($price === FALSE)
                                    $errors[] = t('Attribute option price \'%value\'', array('%value' => check_plain($value))) . $currency_error;
                                else
                                    $node->$key = $price;
                                break;

                            case 'weight':
                                if (!is_numeric($value))
                                    $errors[] = t('Attribute option weight \'%value\' is not valid. It must be a number.', array('%value' => check_plain($value)));
                                else if ($value < 0)
                                    $errors[] = t('Attribute option weight \'%value\' is not valid. It must be greater than or equal to 0.', array('%value' => check_plain($value)));
                                break;

                            case 'ordering':
                                if (!is_intval($value))
                                    $errors[] = t('Attribute option ordering value \'%value\' is not valid. It must be an integer.', array('%value' => check_plain($value)));
                                else if ($value < -10 || $value > 10)
                                    $errors[] = t('Attribute option ordering value \'%value\' is not valid. It must be in the range -10 to 10.', array('%value' => check_plain($value)));
                                break;
                        } //switch ($attr[3])
                    } //if (count($attr) == 3) else
                }
            }
        }
    }
    return $errors;
}

/**
 * Implementation of hook_node_import_postprocess()
 */
function uc_product_node_import_postprocess(&$node, $preview, $error) {
    if (_uc_product_node_import_is_product($node->type) && !$preview && !$error) {
        if (module_exists('uc_attribute')) {
            //Add attribute data to database
            $attribute_enabled = array(); //$attributes[aid][field]
            $attribute_options = array(); //$attribute_options[aid][oid][field]
            foreach ($node as $key => $value) {
                if (stripos($key, 'ucatr') === 0) {
                    $attr = explode(':', $key);
                    if (count($attr) == 3) { //attribute value
                        $attributes[$attr[1]][$attr[2]] = $value;
                    } else { //attribute option value
                        $attribute_options[$attr[1]][$attr[2]][$attr[3]] = $value;
                    }
                }
            }

            $clear_adjustments = false;
            foreach ($attribute_options as $aid => $options) {
                if (isset($attributes[$aid]['enabled']) && $attributes[$aid]['enabled']) {
                    //get default values for attribute
                    $attr_default = db_fetch_object(db_query("SELECT ordering, required FROM {uc_attributes} WHERE aid = %d", $aid));
                    //update ordering and required fields for product
                    if (isset($attributes[$aid]['ordering']))
                        $attr_default->ordering = $attributes[$aid]['ordering'];
                    if (isset($attributes[$aid]['required']))
                        $attr_default->required = $attributes[$aid]['required'] ? 1 : 0;

                    //add attribute to product if necessary
                    if (db_num_rows(db_query("SELECT aid FROM {uc_product_attributes} WHERE nid = %d AND aid = %d", $node->nid, $aid)) == 0) {
                        db_query("INSERT INTO {uc_product_attributes} (nid, aid, ordering, default_option, required, display) SELECT %d, aid, %d, 0, %d, display FROM {uc_attributes} WHERE aid = %d", $node->nid, $attr_default->ordering, $attr_default->required, $aid);
                        //if an attribute was added we must reset the sku attribute adjustments data
                        $clear_adjustments = true;
                    } else { //update product attribute data
                        db_query("UPDATE {uc_product_attributes} SET ordering = %d, required = %d WHERE nid = %d AND aid = %d", $attr_default->ordering, $attr_default->required, $node->nid, $aid);
                    }

                    //set options data
                    foreach ($options as $oid => $fields) {
                        if (isset($fields['enabled'])) {
                            //remove all existing option data
                            db_query("DELETE FROM {uc_product_options} WHERE nid = %d AND oid = %d", $node->nid, $oid);
                            if ($fields['enabled']) {
                                db_query("INSERT INTO {uc_product_options} (nid, oid, cost, price, weight, ordering) VALUES (%d, %d, %f, %f, %f, %d)", $node->nid, $oid, $fields['cost'], $fields['price'], $fields['weight'], $fields['ordering']);
                            } else {
                                $match = 'i:' . $aid . ';s:' . strlen($oid) . ':"' . $oid . '";';
                                db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d AND combination LIKE '%%%s%%'", $node->nid, $match);
                            }
                        }
                    }
                } else { //if ($attribute_enabled[$aid])
                    //remove attribute from product
                    db_query("DELETE FROM {uc_product_attributes} WHERE nid = %d AND aid = %d", $node->nid, $aid);
                    $clear_adjustments = true;
                }
            }

            if ($clear_adjustments)
                db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $node->nid);
        }
    }
}

/**
 * Check whether the node_import $type provided to the hooks is an ubercart product.
 */
function _uc_product_node_import_is_product($type) {
    if (in_array($type, module_invoke_all('product_types'))) {
        return TRUE;
    }
    return FALSE;
}

/**
 * Make a human formatted price (eg $123,456.78) to a plain decimal (eg 123456.78).
 */
function _uc_product_normalize_price($price) {
    $price = trim($price);
    $price = str_replace(variable_get('uc_currency_thou', ','), '', $price);
    $price = str_replace(variable_get('uc_currency_dec', '.'), '.', $price);
    $price = str_replace(variable_get('uc_currency_sign', '$'), '', $price);

    if (empty($price) || is_numeric($price) || preg_match('/^\d*(\.\d*)?$/', $price))
        return $price;
    return FALSE;
}

function is_intval($value) {
    return preg_match('/^[+-]?[0-9]+$/', $value) === 1;
}

function is_bool_value($val) {
    return in_array(strtolower($val), bool_values());
}

function is_true_value($val) {
    return in_array(strtolower($val), true_values());
}

function is_false_value($val) {
    return in_array(strtolower($val), false_values());
}

function bool_values() {
    return array_merge(true_values(), false_values());
}

function true_values() {
    return array('1', 'y', t('yes'), 't', t('true'));
}

function false_values() {
    return array('0', 'n', t('no'), 'f', t('false'));
}

function is_valid_weight_unit($val) {
    return in_array($val, valid_weight_units());
}

function valid_weight_units() {
    return array('lb', 'oz', 'kg', 'g');
}

function is_valid_dimension_unit($val) {
    return in_array($val, valid_dimension_units());
}

function valid_dimension_units() {
    return array('in', 'ft', 'cm', 'mm');
}